package com.zhouz.admin.app.aop;

import com.auth0.jwt.exceptions.TokenExpiredException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhouz.admin.app.db.domain.OptLog;
import com.zhouz.admin.app.db.domain.OptRequestContext;
import com.zhouz.admin.app.db.domain.enums.IsOrNotEnum;
import com.zhouz.admin.app.db.exception.MyRunTimeException;
import com.zhouz.admin.app.db.exception.ReturnBaseException;
import com.zhouz.admin.app.db.service.OptLogService;
import com.zhouz.admin.app.db.service.OptRequestContextService;
import com.zhouz.admin.app.db.vo.LoginUserVo;
import com.zhouz.admin.app.db.vo.Result;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.UUID;

/**
 * 接口切面 记录日志 存入MongoDB
 *
 * @Author 张鹏
 * @Date 2018/8/24 14:21
 */
//@Aspect
//@Component
@Log4j2
public abstract class LogAopBase {
    @Autowired
    private OptLogService logService;
    @Autowired
    private OptRequestContextService optRequestContextService;
    @Autowired
    public HttpServletRequest request;

    public abstract LoginUserVo getLoginUser();
    /**
     * 需要切入的注解
     */
    @Pointcut("@annotation(logAnno)")
    public void initLogAnno(LogAnno logAnno) {
    }

    @Before("initLogAnno(logAnno)")
    public void doBefore(JoinPoint joinPoint, LogAnno logAnno) {
        log.debug("☢☢☢☢☢☢☢☢☢☢☢☢☢☢☢☢☢☢☢☢☢☢☢☢☢☢☢");
        //url
        log.debug("url={}", request.getRequestURL());
        //method
        log.debug("method={}", request.getMethod());
        log.debug("接口名={}",logAnno.urlDesc());
        //ip
        log.debug("ip={}", request.getRemoteAddr());
        //class_method
        log.debug("class_method={}", joinPoint.getSignature().getDeclaringTypeName() + "," + joinPoint.getSignature().getName());
        //args[]
        ObjectMapper mapper = new ObjectMapper();
        try {
            String param = mapper.writeValueAsString(joinPoint.getArgs());
            log.info("args={}", param);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }
    @Around("initLogAnno(logAnno)")
    public Object around(ProceedingJoinPoint pjp, LogAnno logAnno) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object o = null;
        //暂时使用notes获取 businessNo的值
        String businessNoKey = logAnno.businessNo();
        String urlDesc = logAnno.urlDesc();
        String app = logAnno.app();
        //获取businessNo的值
        String businessNo = "";
        if (StringUtils.isNotBlank(businessNoKey)) {
            StandardEvaluationContext standardEvaluationContext = new StandardEvaluationContext(pjp.getArgs());
            standardEvaluationContext = setContextVariables(standardEvaluationContext, pjp);
            businessNo = getElValue(businessNoKey, standardEvaluationContext);
        }
        LoginUserVo loginUserVo = this.getLoginUser();
        try {
            o = pjp.proceed();
            OptLog logs = new OptLog(urlDesc, (int) (System.currentTimeMillis() - startTime), businessNo, IsOrNotEnum.NO,logAnno.app());
            this.save2log(pjp.getArgs(), logs, o, null,loginUserVo);
            return o;
        }catch (IllegalArgumentException ie){
            return Result.badArgument().msg(ie.getMessage());
        }catch (MethodArgumentTypeMismatchException e){
            throw e;
        }catch (ReturnBaseException e){
            throw e;
        }
        // token重新刷新
        catch (TokenExpiredException te){
            throw te;
        }
        catch (Exception e) {
            log.error("☢☢☢☢☢☢☢☢☢报错☢☢☢☢☢☢☢☢☢☢☢");
            log.info("class_method={}", pjp.getSignature().getDeclaringTypeName() + "," + pjp.getSignature().getName());
            try {
                ObjectMapper mapper = new ObjectMapper();
                String param = mapper.writeValueAsString(pjp.getArgs());
                log.info("args={}", param);
            } catch (JsonProcessingException je) {
                e.printStackTrace();
            }
            log.error("异常信息",e);
            //其他运行异常统一处理
            log.error(e.toString() + "：" + e.getMessage());
            OptLog logs = new OptLog(urlDesc, (int) (System.currentTimeMillis() - startTime), businessNo, IsOrNotEnum.YES,app);
            // 主动抛出异常，进行事务回滚
            throw new MyRunTimeException(logs,pjp.getArgs(),e.getMessage(),loginUserVo);
        }
    }

    private void save2log(Object[] args, OptLog logs, Object res, Exception e,LoginUserVo loginInfoDto) {
        String url = request.getRequestURL().toString();
        String ip = request.getRemoteAddr();
        new Thread(() -> {
            logs.setUrl(url);
            logs.setRequestNo(UUID.randomUUID().toString().replaceAll("-", ""));
            logs.setIp(ip);
            if(loginInfoDto != null){
                logs.setUserName(loginInfoDto.getUserName());
                logs.setUserCode(loginInfoDto.getUserId());
            }
            logService.save(logs);
            //添加详情
            OptRequestContext context = new OptRequestContext();
            ObjectMapper mapper = new ObjectMapper();
            try {
                String param = mapper.writeValueAsString(args);
                String result = mapper.writeValueAsString(res);
                context.setInParamter(param);
                context.setOutParamter(result);
                context.setRequestNo(logs.getRequestNo());
                if (e != null) {
                    context.setException(e.getMessage());
                }
                optRequestContextService.save(context);
            } catch (JsonProcessingException ex) {
                ex.printStackTrace();
            }
        }).start();
    }

    private StandardEvaluationContext setContextVariables(StandardEvaluationContext standardEvaluationContext, ProceedingJoinPoint joinPoint) {

        Object[] args = joinPoint.getArgs();
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method targetMethod = methodSignature.getMethod();
        LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
        String[] parametersName = parameterNameDiscoverer.getParameterNames(targetMethod);

        if (args == null || args.length <= 0) {
            return standardEvaluationContext;
        }
        for (int i = 0; i < args.length; i++) {
            standardEvaluationContext.setVariable(parametersName[i], args[i]);
        }
        return standardEvaluationContext;
    }

    /**
     * 通过key SEL表达式获取值
     *
     * @param key
     * @param context
     * @return
     * @Author 张鹏
     * @Date 2018/8/24 15:22
     */
    private String getElValue(String key, StandardEvaluationContext context) {
        if (StringUtils.isBlank(key)) {
            return "";
        }
        ExpressionParser parser = new SpelExpressionParser();
        Expression exp = parser.parseExpression(key);
        String value = exp.getValue(context, String.class);

        return value;

    }
}
